package com.lcf.mybatisplus.resolver;

import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static com.lcf.base.constant.SysConstant.SESSION_TENANT_ID_KEY;

/**
 * 根据表名判断是否需要进行多租户限制
 * 对添加有@TableId注解的类进行扫描，判断其是否有tenantId属性
 *
 * @author lcf
 */
@Slf4j
@Component
@ConditionalOnClass(name = "com.baomidou.mybatisplus.annotation.TableId")
public class TableEntityResolver implements InitializingBean {

    private final Map<String, Class<?>> tableEntityMap = new ConcurrentHashMap<>(16);
    private final Map<String, Boolean> tableTenantIdCache = new ConcurrentHashMap<>(16);
    private static final String[] ENTITY_PACKAGES = {"com.lcf"};

    @Value("${mybatis-plus.global-config.db-config.table-prefix:}")
    private String tablePrefix;

    @Override
    public void afterPropertiesSet() throws Exception {
        initializeTableEntityMap();
    }

    private void initializeTableEntityMap() throws ClassNotFoundException {
        ClassPathScanningCandidateComponentProvider scanner =
                new ClassPathScanningCandidateComponentProvider(false);

        // 移除原来的两个IncludeFilter，使用一个合并后的过滤器
        scanner.addIncludeFilter(new TypeFilter() {
            private final Map<String, Boolean> cache = new HashMap<>(128);

            @Override
            public boolean match(@NotNull MetadataReader metadataReader, @NotNull MetadataReaderFactory metadataReaderFactory) {
                String className = metadataReader.getClassMetadata().getClassName();

                // 先检查缓存
                if (cache.containsKey(className)) {
                    return cache.get(className);
                }

                try {
                    Class<?> currentClass = Class.forName(className);
                    boolean hasTableId = false;
                    while (currentClass != null && currentClass != Object.class) {
                        Field[] fields = currentClass.getDeclaredFields();
                        for (Field field : fields) {
                            if (field.isAnnotationPresent(TableId.class)) {
                                hasTableId = true;
                                break;
                            }
                        }
                        if (hasTableId) {
                            break;
                        }
                        currentClass = currentClass.getSuperclass() == Object.class ? null : currentClass.getSuperclass();
                    }

                    cache.put(className, hasTableId);
                    return hasTableId;
                } catch (Exception e) {
                    log.error("检查类失败", e);
                    cache.put(className, false);
                    return false;
                }
            }
        });

        for (String packageName : ENTITY_PACKAGES) {
            Set<org.springframework.beans.factory.config.BeanDefinition> candidates =
                    scanner.findCandidateComponents(packageName);

            for (org.springframework.beans.factory.config.BeanDefinition candidate : candidates) {
                String className = candidate.getBeanClassName();
                if (StringUtils.isBlank(className)) {
                    continue;
                }
                Class<?> entityClass = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader());

                if (entityClass.isInterface() || isAbstract(entityClass) || entityClass.isMemberClass()) {
                    continue;
                }

                String tableName = resolveTableName(entityClass);
                String finalTableName = addTablePrefix(tableName).toLowerCase();
                tableEntityMap.put(finalTableName, entityClass);
                tableTenantIdCache.put(finalTableName, hasTenantIdProperty(entityClass));
            }
        }
    }

    /**
     * 解析实体类对应的表名
     */
    private String resolveTableName(Class<?> entityClass) {
        TableName tableNameAnnotation = entityClass.getAnnotation(TableName.class);
        if (tableNameAnnotation != null && StringUtils.isNotBlank(tableNameAnnotation.value())) {
            return tableNameAnnotation.value();
        }
        return com.baomidou.mybatisplus.core.toolkit.StringUtils.camelToUnderline(entityClass.getSimpleName());
    }

    /**
     * 为表名添加全局前缀
     * 优化点：处理前缀可能包含的下划线，避免重复拼接
     */
    private String addTablePrefix(String tableName) {
        if (StringUtils.isEmpty(tablePrefix)) {
            return tableName;
        }
        // 处理表名已包含前缀的情况（如前缀为"sys_"，表名为"sys_user"则不重复添加）
        return tableName.startsWith(tablePrefix) ? tableName : tablePrefix + tableName;
    }

    /**
     * 根据表名获取对应的实体类
     */
    public Class<?> getEntityClassByTableName(String tableName) {
        return StringUtils.isNotBlank(tableName) ? tableEntityMap.get(tableName.toLowerCase()) : null;
    }

    /**
     * 判断实体类（含父类）是否包含tenantId属性
     */
    public boolean hasTenantIdProperty(Class<?> entityClass) {
        if (entityClass == null) {
            return false;
        }

        Class<?> currentClass = entityClass;
        while (currentClass != Object.class) {
            try {
                // 只要不抛异常，就说明字段存在
                currentClass.getDeclaredField(SESSION_TENANT_ID_KEY);
                return true;
            } catch (NoSuchFieldException e) {
                currentClass = currentClass.getSuperclass();
            }
        }
        return false;
    }

    /**
     * 快捷方法：根据表名判断是否包含tenantId属性
     * 优化点：直接查询缓存，避免运行时反射
     */
    public boolean hasTenantIdByTableName(String tableName) {
        return StringUtils.isNotBlank(tableName) && tableTenantIdCache.getOrDefault(tableName.toLowerCase(), false);
    }

    /**
     * 判断类是否为抽象类
     */
    private boolean isAbstract(Class<?> clazz) {
        return (clazz.getModifiers() & java.lang.reflect.Modifier.ABSTRACT) != 0;
    }
}
